#ifndef CLIENT_H
#define CLIENT_H

#include "net.h"
#include "file.h"

class Protocol;
class PackageDescription;

#ifndef CIO_IBUFSIZE
#define CIO_IBUFSIZE	4096
#endif
#ifndef CIO_OBUFSIZE
#define CIO_OBUFSIZE	4096
#endif

class Client : public NetCB, public FileCB
{
	friend class DetermineRequest;

	public:
		Client(HANDLE net);
		~Client();

		bool8 initOk();
		void ready();
		void close();
		bool8 isStale(uint32 staleAge);

		void readNet();
		void writeNet();
		void protocolDone();
		void errmsg(int32 Error);
		bool8 isDatatoRead(int32 Amt, int32 index, uint8 val);
		void setBytesPerSecond(int32 bps);

		Net* getNet();
		File* getFile();

		uint8* getIBuf();
		int32 getISize();
		void  eatIBuf(int32 Amt);
		int32 getIUsed();

		uint8* getOBuf();
		int32 getOSize();
		int32 getOUsed();
		void  setOUsed(int32 Amt);
		
		void netIn(int32 Amt);
		void netOut(int32 Amt);
		void netErr(uint32 Err);
		void netTimer(int32 Amt);

		void fileIn(int32 Amt);
		void fileEOF();
		void fileErr(uint32 Err);

		Client* getNext();
		Client* getPrev();
		void setNext(Client*);
		void setPrev(Client*);

        bool8 isClosed();

		void SetPackage(PackageDescription *desc);
		PackageDescription *GetPackage();

		int32 getMsgLen();

		void msgStart();
		void msgAdd(uint8 Byte);
		void msgAdd(int32 amt);
		void msgAdd(uint8* buf, int32 len=0);
		void msgAdd(uint16 amt);
		void msgAdd(const String& str, bool8 unicode=false);

		uint8* msgGetBuf();
		void msgReleaseBuf(int32 Amt);
		bool8 msgEnd();

		uint8	msgGet(uint8 * buffer, uint8 * data);
		uint16	msgGet(uint8 * buffer, uint16 * data);
		uint32	msgGet(uint8 * buffer, uint32 * data);

		enum MsgFormats
		{
			SINGLE_BYTE,
			DOUBLE_BYTE
		};

		int32 getMsgFormat();
		void setMsgFormat(int32 format);
		int32 getMsgOverhead();
		void setMsgOverhead(int32 overhead);

        void netLock();
        void netUnlock();

        void fileLock();
        void fileUnlock();

	protected:
		void realNetOut(int32 Amt);

		int32 encode(uint8* in, uint8* out, int32 len);
		Client	*next, *prev;

		Net* net;
		File* file;

		enum IOBufSizes { INMAX = CIO_IBUFSIZE, OUTMAX = CIO_OBUFSIZE };
		uint8 in[2 * INMAX + 1];
		uint8 out[2 * OUTMAX + 1];
		uint8 msgbuf[2 * OUTMAX + 1];
		int32 msglen;
		int32 iused;
		int32 oused;
		uint32 reqTime;
		int32 msgFormat;
		int32 bytespersec;
		int32 subsend;
		uint32 endtime;
		uint32 msgOverhead;

		enum Protocols { 
			IDLE = 0,
			REQ_LIST, REQ_DOWNLOAD,
			REQ_PKG_FILELIST, REQ_RSYNC,
			RECV_PKG_UPDATE, SPEEDTEST,
			CHANGESPEED,
			NUMPROTOCOLS };

		Protocol*		handler[NUMPROTOCOLS];
		uint32			protocol;

		PackageDescription *m_current_package;

        bool8 m_isClosed;
        PCRITICAL_SECTION m_pNetCritSec;
        PCRITICAL_SECTION m_pFileCritSec;
};      

inline Net*
Client::getNet()
{
	return net;
}

inline File*
Client::getFile()
{
	return file;
}

inline uint8*
Client::getIBuf()
{
	return in;
}

inline int32
Client::getISize()
{
	return INMAX;
}

inline uint8*
Client::getOBuf()
{
	return out;
}

inline int32
Client::getOSize()
{
	return OUTMAX;
}

inline void 
Client::setOUsed(int32 Amt)
{
	//ASSERT(Amt < 256);
	ASSERT(Amt >= 0 && Amt <= OUTMAX);
	oused = Amt;
}

inline Client*
Client::getNext()
{
	return next;
}

inline Client*
Client::getPrev()
{
	return prev;
}

inline void
Client::setNext(Client* Next)
{
	next = Next;
}

inline void
Client::setPrev(Client* Prev)
{
	prev = Prev;
}

inline void 
Client::SetPackage(PackageDescription *desc)
{
	m_current_package = desc;
}

inline PackageDescription *
Client::GetPackage()
{
	return m_current_package;
}

inline int32 
Client::getIUsed()
{
	return iused;
}

inline bool8
Client::isClosed()
{
	return m_isClosed;
}
		
inline int32
Client::getMsgLen()
{
	return msglen;
}

inline int32
Client::getOUsed()
{
	return oused;
}

inline int32 
Client::getMsgFormat()
{
	return msgFormat;
}

inline void
Client::setMsgFormat(int32 format)
{
	msgFormat = format;
}

inline int32
Client::getMsgOverhead()
{
	return msgOverhead;
}

inline void
Client::setMsgOverhead(int32 overhead)
{
	msgOverhead = overhead;
}

inline bool8
Client::isDatatoRead(int32 Amt, int32 index, uint8 val)
{
	return net->isDatatoRead(Amt, index, val);
}

inline void
Client::setBytesPerSecond(int32 bps)
{
    static const int MIN_BPS = 5000;

    if ( bps > 0  &&  bps < MIN_BPS )
    {
        bps = MIN_BPS;
    }

    if ( bytespersec != bps )
    {
        DEBUGMSG(("Changing bytes/sec from %d to %d", bytespersec, bps));
    }

	bytespersec = bps;

	if (bytespersec <= 0)	//ignore old endtime
		endtime = 0;
}

#endif
